package cn.ava.basic.service.impl;

import cn.ava.basic.constant.BaseConstants;
import cn.ava.basic.dto.EmailRegisterDto;
import cn.ava.basic.dto.SmsRegisterDto;
import cn.ava.basic.exception.BusinessException;
import cn.ava.basic.service.IVerifyCodeService;
import cn.ava.basic.utils.*;
import cn.ava.user.domain.User;
import cn.ava.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired // redis连接
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired // user表map层
    private UserMapper userMapper;


    /**
     * @Title: getGraph
     * @Description: 获取随机四位图形验证码
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/11 15:14
     * @param:
     * @return:
     */
    @Override
    public String getGraph(String imageCodeKey) {
        // 判断参数是否为空
        if (StringUtils.isEmpty(imageCodeKey)) {
            throw new BusinessException("参数错误！");
        }
        // 调用字符串工具类获取随机四位数字符串
        String code = StrUtils.getComplexRandomString(4);
        // 将随机数设置进redis中
        redisTemplate.opsForValue().set(imageCodeKey, code, 5, TimeUnit.MINUTES);
        // 将code值转换为图形并编译成Base64
        return VerifyCodeUtils.VerifyCode(100, 35, code);
    }

    /**
     * @Title: smsCode
     * @Description: 发送短信验证码
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/6 14:19
     * @param:
     * @return:
     */
    @Override
    public void smsCode(SmsRegisterDto smsRegisterDto) {
        // 用户填写的手机号
        String phone = smsRegisterDto.getPhone();
        // 业务类型
        String type = smsRegisterDto.getType();
        // 用户填写的验证码
        String imageCodeValue = smsRegisterDto.getImageCodeValue();
        // 前端传递用于获取图验证码的key
        String imageCodeKey = smsRegisterDto.getImageCodeKey();
        if (StringUtils.isEmpty(type)){
            throw new BusinessException("参数异常!");
        }
        // 判断业务类型
        if (type.equals("register")) { // 注册短信
            // 调用非空校验工具类
            CheckCodeUtil.checkEmpty(phone,type,imageCodeKey,imageCodeValue);
            // 正则表达式校验手机号格式是否正确
            if (!Pattern.matches(BaseConstants.VerifyShopSettlement.REGEX_MOBILE,phone)) {
                throw new BusinessException("请输入正确的手机号,请重新输入");
            }
            // 判断图形验证码是否正确
            checkImageCode(imageCodeKey,imageCodeValue);
            // 判断用户是否已经被注册
            isPhoneRegister(phone);
            // 发送注册短信
            sendSmsCode(BaseConstants.VerifyCodeConstant.BUSINESS_REGISTER_KEY, phone);
        }else if(type.equals("binder")) { // 绑定账号短信
            // 调用非空校验工具类
            CheckCodeUtil.checkEmpty(phone);
            // 发送绑定平台用户短信验证码
            sendSmsCode(BaseConstants.VerifyCodeConstant.BUSINESS_BINDER_KEY, phone);
        }else if(type.equals("login")) {
            // 调用非空校验工具类
            CheckCodeUtil.checkEmpty(phone);
            // 判断图形验证码是否正确
            checkImageCode(imageCodeKey,imageCodeValue);
            // 发送手机短信登录验证码
            sendSmsCode(BaseConstants.VerifyCodeConstant.BUSINESS_LOGIN_KEY, phone);
        }
    }

    /**
     * @Title: isPhoneRegister
     * @Description: 判断用户是否被注册
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/11 12:07
     * @param:
     * @return:
     */
    public void isPhoneRegister(String phone) {
        User user = userMapper.getByPhone(phone);
        if(user != null)
        {
            throw new BusinessException("用户已被注册！请直接登录或更换用户名");
        }
    }


    /**
     * @Title: checkImageCode
     * @Description: 图形验证码校验
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/11 12:08
     * @param:
     * @return:
     */
    public void checkImageCode(String imageCodeKey,String imageCodeValue) {
        // 调用非空校验工具类
        CheckCodeUtil.checkEmpty(imageCodeKey,imageCodeValue);
        // redis中获取对应的value值,判断图形验证码是否存在
        Object imageVerify = redisTemplate.opsForValue().get(imageCodeKey);
        //
        if (imageVerify == null) {
            throw new BusinessException("图片验证码已过期，请重新点击获取！");
        }
        // 如果验证码存在，判断是否正确
        if (!imageVerify.toString().equalsIgnoreCase(imageCodeValue)) {
            throw new BusinessException("验证码错误！请重新输入");
        }

    }

    /**
     * @Title: sendSmsCode
     * @Description:
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/11 15:16
     * @param:
     * @return:
     */
    public void sendSmsCode(String businessKey, String phone) {
        // 使用业务键拼接手机号从redis中获取手机验证码
        String smsCodeKey = businessKey + phone;
        // 从redis中查询
        Object smsCodeValueRedis = redisTemplate.opsForValue().get(smsCodeKey);
        // 创建一个口的字符串对象用于存放重新发送给用户的验证码
        String smsCode = "";
        // 判断验证码是否为空
        if (smsCodeValueRedis == null) {
            // 如果为空，说明是第一次获取，直接生成新的验证码
            smsCode = StrUtils.getComplexRandomString(4);
        }else { // 如果不为空，说明redis中存在验证码,需要判断发送短信验证码的时间是否超过一分钟
            // 通过业务键截取到时间戳，判断是否超过一分钟
            String[] smsCodeSplit = smsCodeValueRedis.toString().split(":");
            // 获取到老的时间戳
            Long oldTime = Long.valueOf(smsCodeSplit[1]);
            // 获取当前时间戳
            Long nowTime = System.currentTimeMillis();
            if (nowTime - oldTime <= 60 * 1000) {
                throw new BusinessException("发送时间小于一分钟，请稍后重试！");
            }
            // redis中存在此验证码，直接使用之前的验证码并重置时间戳
            smsCode = smsCodeSplit[0];
        }
        // 将验证码存在redis中，设置过期时间为三分钟，并拼接上时间戳
        String smsCodeValue =  smsCode +":"+ System.currentTimeMillis();
        redisTemplate.opsForValue().set(smsCodeKey,smsCodeValue,3,TimeUnit.MINUTES);
        // 发送短信验证码
//        SendSmsUtil.SendSms(phone,"尊敬的用户您好,\n您的手机验证码为: " + smsCode);
        System.err.println(smsCode);
    }


    /**
     * @Title: emailCode
     * @Description: 邮箱验证码
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/7/8 23:29
     * @param:  
     * @return: 
     */
    @Override
    public void sendMailCode(EmailRegisterDto emailRegisterDto) {
        // 用户填写的邮箱
        String email = emailRegisterDto.getEmail();
        // redis中邮箱验证码的key
        String emailCodeKey = emailRegisterDto.getEmailCodeKey();
        // 为空判断
        if(StringUtils.isEmpty(email.trim())
                ||StringUtils.isEmpty(emailCodeKey.trim())){
            throw new BusinessException("参数错误！请重新填写");
        }
        // 调用字符串工具类获取随机六位数字符串
        String code = StrUtils.getComplexRandomString(6);
        // 将随机数设置进redis中
        redisTemplate.opsForValue().set(emailCodeKey,code,5, TimeUnit.MINUTES);
        // 判断用户是否已经被注册
        User user = userMapper.getByEmail(email);
        if (user != null) {
            throw new BusinessException("用户已被注册！请直接登录或更换用户名");
        }
        // 正则表达式校验手机号格式是否正确
        if(!Pattern.matches(BaseConstants.VerifyShopSettlement.REGEX_EMAIL,email)){
            throw new BusinessException("请输入正确的邮箱,请重新输入");
        }
        // redis中获取对应的value值,判断邮箱验证码是否存在
        Object emailVerify = redisTemplate.opsForValue().get(emailCodeKey);
        if (emailVerify == null) {
            throw new BusinessException("验证码已过期，请重新获取！");
        }
        // 使用业务键拼接手机号从redis中获取手机验证码
        String emailRedisKey = BaseConstants.VerifyCodeConstant.BUSINESS_REGISTER_KEY + email;
        // 从redis中查询
        Object emailCodeValueRedis = redisTemplate.opsForValue().get(emailRedisKey);
        // 创建一个口的字符串对象用于存放重新发送给用户的验证码
        String emailCode = "";
        // 判断验证码是否为空
        if (emailCodeValueRedis == null) {
            // 如果为空，说明是第一次获取，直接生成新的验证码
            emailCode = StrUtils.getComplexRandomString(6);
        }else { // 如果不为空，说明redis中存在验证码,需要判断发送短信验证码的时间是否超过一分钟
            // 通过业务键截取到时间戳，判断是否超过一分钟
            String[] emailCodeSplit = emailCodeValueRedis.toString().split(":");
            // 获取到老的时间戳
            Long oldTime = Long.valueOf(emailCodeSplit[1]);
            // 获取当前时间戳
            Long nowTime = System.currentTimeMillis();
            if (nowTime - oldTime <= 60 * 1000) {
                throw new BusinessException("发送时间小于一分钟，请稍后重试！");
            }
            // 如果redis中存在此验证码，那么使用之前的验证码并重置时间戳
            emailCode = emailCodeSplit[0];
        }
        // 将验证码存在redis中，设置过期时间为三分钟，并拼接上时间戳
        String emailRedisValue =  emailCode +":"+ System.currentTimeMillis();
        redisTemplate.opsForValue().set(emailRedisKey,emailRedisValue,5,TimeUnit.MINUTES);
        SendmailUtil.sendMail(
                "2353421050@qq.com",email,
                "尊敬的用户,您好:",
                "<br>您正在注册【宠物之家平台】账号，您的验证码为：<br>" + emailCode,
                true);
        System.err.println(emailCode);
    }



}
